#include "stdafx.h"
#include "CppUnitTest.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

#include <HSModel/FindWaterAreas.h>
#include <HSModel/HeightMap.h>
#include <HSModel/Model3D.h>

#include <algorithm>

namespace HSModelTests
  {
  TEST_CLASS(FindWaterAreasTests)
    {
    public:
      TEST_METHOD(SmallHeightMapWaterFindingTest)
        {
        using namespace HeightMapUtils;
        HeightMap hm(3, 3);

        /*
        Water level: 4

        [3] 6  6
        [2  3] 6
        [3] 6  5

        */

        hm.Set(0, 0, 3);
        hm.Set(0, 1, 6);
        hm.Set(0, 2, 6);

        hm.Set(1, 0, 2);
        hm.Set(1, 1, 3);
        hm.Set(1, 2, 6);

        hm.Set(2, 0, 3);
        hm.Set(2, 1, 6);
        hm.Set(2, 2, 5);

        std::vector<THMapArea> areas = FindPossibleWaterAreas(hm, 4);


        THMapPoint _points[4] = {THMapPoint(0, 0), THMapPoint(1, 0), THMapPoint(1, 1), THMapPoint(2, 0)};
        auto points = std::set<THMapPoint>(_points, _points + 4);

        Assert::AreEqual(1, int(areas.size()));
        bool found = (areas[0].level == 4) && (areas[0].points == points);
        Assert::IsTrue(found);
        }

      TEST_METHOD(LargerMapWaterFindingTest)
        {

        HeightMap hm(6, 6);

        /*
        Water level: 5
        
         6  6  6  6  8  8
        [3  2] 6 [2] 8  8
         7  6  7 [3  3] 8
         6 [4  4] 7 [3] 8
         6 [3  5] 7  7  8
         7 [5] 6  8  8 9
        */

        hm.Set(0, 0, 6);
        hm.Set(0, 1, 6);
        hm.Set(0, 2, 6);
        hm.Set(0, 3, 6);
        hm.Set(0, 4, 8);
        hm.Set(0, 5, 8);


        hm.Set(1, 0, 3);
        hm.Set(1, 1, 2);
        hm.Set(1, 2, 6);
        hm.Set(1, 3, 2);
        hm.Set(1, 4, 8);
        hm.Set(1, 5, 8);

        hm.Set(2, 0, 7);
        hm.Set(2, 1, 6);
        hm.Set(2, 2, 7);
        hm.Set(2, 3, 3);
        hm.Set(2, 4, 3);
        hm.Set(2, 5, 8);

        hm.Set(3, 0, 6);
        hm.Set(3, 1, 4);
        hm.Set(3, 2, 4);
        hm.Set(3, 3, 7);
        hm.Set(3, 4, 3);
        hm.Set(3, 5, 8);

        hm.Set(4, 0, 6);
        hm.Set(4, 1, 3);
        hm.Set(4, 2, 5);
        hm.Set(4, 3, 7);
        hm.Set(4, 4, 7);
        hm.Set(4, 5, 8);

        hm.Set(5, 0, 7);
        hm.Set(5, 1, 5);
        hm.Set(5, 2, 6);
        hm.Set(5, 3, 8);
        hm.Set(5, 4, 8);
        hm.Set(5, 5, 9);

        auto areas = HeightMapUtils::FindPossibleWaterAreas(hm, 5);

        Assert::AreEqual(3, int(areas.size()));
        }

      TEST_METHOD(GetWaterModelsGeneralTest)
        {
        HeightMap hm(3, 3);

        hm.Set(0, 0, 6);
        hm.Set(0, 1, 6);
        hm.Set(0, 2, 6);

        hm.Set(1, 0, 6);
        hm.Set(1, 1, 3);
        hm.Set(1, 2, 6);

        hm.Set(2, 0, 6);
        hm.Set(2, 1, 6);
        hm.Set(2, 2, 6);

        auto models = HeightMapUtils::GetWaterModels(hm, 4);
        
        Assert::AreEqual(1, (int) models.size());
        int c = 0;
        for(auto it = models[0]->GetModel().Triangles().begin(); it != models[0]->GetModel().Triangles().end(); it++)
          {
          c++;
          }

        Assert::AreEqual(6, c);
        }

      TEST_METHOD(GetWatersModelsProducesOneTriangleForCornerCase)
        {
        HeightMap hm(3, 3);

        hm.Set(0, 0, 6);
        hm.Set(0, 1, 6);
        hm.Set(0, 2, 6);

        hm.Set(1, 0, 6);
        hm.Set(1, 1, 6);
        hm.Set(1, 2, 6);

        hm.Set(2, 0, 6);
        hm.Set(2, 1, 6);
        hm.Set(2, 2, 3);

        auto models = HeightMapUtils::GetWaterModels(hm, 4);

        Assert::AreEqual(1, (int) models.size());
        int c = 0;
        for(auto it = models[0]->GetModel().Triangles().begin(); it != models[0]->GetModel().Triangles().end(); it++)
          {
          c++;
          }

        Assert::AreEqual(1, c);
        }

      TEST_METHOD(WaterModelsDoNotContainUnnecessaryPoints)
        {
        HeightMap hm(3, 3);

        hm.Set(0, 0, 6);
        hm.Set(0, 1, 6);
        hm.Set(0, 2, 6);

        hm.Set(1, 0, 6);
        hm.Set(1, 1, 3);
        hm.Set(1, 2, 6);

        hm.Set(2, 0, 6);
        hm.Set(2, 1, 6);
        hm.Set(2, 2, 6);

        auto models = HeightMapUtils::GetWaterModels(hm, 4);

        Assert::AreEqual(1, (int) models.size());

        std::set<TVector3> points;
        for(auto it = models[0]->GetModel().Triangles().begin(); it != models[0]->GetModel().Triangles().end(); it++)
          {
          points.insert(it->GetPoint(0));
          points.insert(it->GetPoint(1));
          points.insert(it->GetPoint(2));
          }

        size_t c_p = 0;
        for(auto it = models[0]->GetModel().Points().begin(); it != models[0]->GetModel().Points().end(); it++)
          {
          c_p++;
          }

        Assert::AreEqual(c_p, points.size());
        }

      TEST_METHOD(ShouldGenerateWaterModel)
        {
        HeightMap hm(3, 3);

        hm.Set(0, 0, 6);
        hm.Set(0, 1, 6);
        hm.Set(0, 2, 6);

        hm.Set(1, 0, 6);
        hm.Set(1, 1, 3);
        hm.Set(1, 2, 6);

        hm.Set(2, 0, 6);
        hm.Set(2, 1, 6);
        hm.Set(2, 2, 6);

        auto models = HeightMapUtils::GetWaterModels(hm, 4);
        THMapArea water_area;
        std::vector<THMapArea> water_areas = HeightMapUtils::FindPossibleWaterAreas(hm, 4);
        
        size_t areas_count_expected = 1;
        size_t areas_count_actual = water_areas.size();
        Assert::AreEqual(areas_count_expected, areas_count_actual);
        
        std::shared_ptr<Model3D> p_water_model = HeightMapUtils::GenerateWaterModel(water_areas[0], hm.GetSizeX(), hm.GetSizeY());
        
        int c = 0;
        for(auto it = p_water_model->Triangles().begin(); it != p_water_model->Triangles().end(); it++)
          {
          c++;
          }

        Assert::AreEqual(6, c);
        }

      TEST_METHOD(ShouldFindHugeWaterArea)
        {
        using namespace HeightMapUtils;
        size_t size_x = 96;
        size_t size_y = 96;

        HeightMap hm(size_x, size_y);
        for (size_t i = 0; i < size_x; ++i)
          for (size_t j = 0 ; j < size_y; ++j)
            hm.Set(i, j, -1.0);

        double water_level = 0.0;
        std::vector<std::shared_ptr<Water>> water_models = GetWaterModels(hm, water_level);

        size_t water_count_expected = 1;
        size_t water_count_actual = water_models.size();

        Assert::AreEqual(water_count_expected, water_count_actual);
        }

    };
  }